package top.jfunc.common.propenv;

import top.jfunc.common.utils.CharsetUtil;
import top.jfunc.common.utils.IoUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 封装Properties
 * @author 熊诗言
 * @see BaseEnvStream#loadEnvInputStream(String)
 */
public class Prop{
    /**
     * ${xxx}的正则表达式
     */
    private static final Pattern REPLACE_PATTERN = Pattern.compile("\\$\\{[^}]+\\}");

    private Properties properties;

    public Prop(Properties properties) {
        this.properties = properties;
    }

    public Prop(InputStream inputStream, String encoding) {
        try (InputStreamReader reader = new InputStreamReader(inputStream, encoding)){
            this.properties = new Properties();
            this.properties.load(reader);
        } catch (IOException var12) {
            throw new RuntimeException("Fail to loading properties file.", var12);
        }
    }
    public Prop(InputStream inputStream) {
        this(inputStream, CharsetUtil.UTF_8);
    }

    public Prop(File file, String encoding) {
        this(IoUtil.getFileInputStream(file), encoding);
    }
    public Prop(File file) {
        this(IoUtil.getFileInputStream(file), CharsetUtil.UTF_8);
    }

    public String get(String key) {
        String value = this.properties.getProperty(key);
        if(null == value){
            return null;
        }
        //包含${xx}
        if(value.contains("${") && value.contains("}")){
            Matcher matcher = REPLACE_PATTERN.matcher(value);
            while (matcher.find()){
                //${xx}
                String group = matcher.group();
                //xx
                String tempKey = group.substring(2, group.length() - 1);
                String replacement = get(tempKey);
                if(null == replacement){
                    throw new IllegalStateException("未找到:"+tempKey);
                }
                value = value.replace(group, replacement);
            }
        }
        return value;
    }

    public String get(String key, String defaultValue) {
        String value = get(key);
        return null != value ? value : defaultValue;
    }

    public Integer getInt(String key) {
        return this.getInt(key, null);
    }

    public Integer getInt(String key, Integer defaultValue) {
        String value = get(key);
        return value != null ? Integer.valueOf(value) : defaultValue;
    }

    public Long getLong(String key) {
        return this.getLong(key, null);
    }

    public Long getLong(String key, Long defaultValue) {
        String value = get(key);
        return value != null ? Long.valueOf(value) : defaultValue;
    }

    public Double getDouble(String key) {
        return this.getDouble(key, null);
    }

    public Double getDouble(String key, Double defaultValue) {
        String value = get(key);
        return value != null ? Double.valueOf(value) : defaultValue;
    }

    public Float getFloat(String key) {
        return this.getFloat(key, null);
    }

    public Float getFloat(String key, Float defaultValue) {
        String value = get(key);
        return value != null ? Float.valueOf(value) : defaultValue;
    }

    public Boolean getBoolean(String key) {
        return this.getBoolean(key, null);
    }

    public Boolean getBoolean(String key, Boolean defaultValue) {
        String value = get(key);
        return value != null ? Boolean.valueOf(value) : defaultValue;
    }

    /**
     * merge其他的prop，返回新的prop，参数的prop优先级更高
     * @param prop 另外的prop
     * @return this
     */
    public Prop merge(Prop prop){
        Properties ps = new Properties();
        for (Map.Entry<Object, Object> entry : this.getProperties().entrySet()) {
            ps.put(entry.getKey(), entry.getValue());
        }
        //后面如果key相同就覆盖前面的
        for (Map.Entry<Object, Object> entry : prop.getProperties().entrySet()) {
            ps.put(entry.getKey(), entry.getValue());
        }
        return new Prop(ps);
    }

    public boolean containsKey(String key) {
        return this.properties.containsKey(key);
    }

    public Properties getProperties() {
        return this.properties;
    }

    public Enumeration<Object> keys(){
        return this.properties.keys();
    }

    public Set<String> stringKeys(){
        return this.properties.stringPropertyNames();
    }

    public Map<String, String> toMap(){
        Map<String, String> map = new HashMap<>(this.properties.size());
        for (String name : this.properties.stringPropertyNames()) {
            map.put(name, get(name));
        }
        return map;
    }

    @Override
    public String toString() {
        return this.properties.toString();
    }
}